﻿using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml;
using D = DocumentFormat.OpenXml.Drawing;
using P = DocumentFormat.OpenXml.Presentation;
using PK = DocumentFormat.OpenXml.Packaging;
using Fantom.Helper;
using Fantom.Drawing;
using Fantom.Models;

namespace Fantom.Builders
{
	/// <summary>
	/// 图形的构建器。
	/// </summary>
	internal class ShapeBuilder
	{
		private static List<string> _shapeMarks = new List<string> { "sp", "cxnSp", "grpSp", "pic" };

		public static IShape Build
			(PK.SlidePart sldpart, OpenXmlElement element, Theme theme, IMediaManager mediaManager)
		{
			var shp = new Shape();

			var xmlname = element.XmlQualifiedName.Name;

			if (!_shapeMarks.Contains(xmlname)) return null;

			var nvpr = element.FirstChild;
			var cnvpr = nvpr.ChildElements[0] as P.NonVisualDrawingProperties;

			var cnvobpr = nvpr.ChildElements[1];
			var obLocks = cnvobpr.FirstChild;

			var nvobpr = nvpr.ChildElements[2];

			var sppr = element?.GetFirstChild<P.ShapeProperties>(); // 获得图形属性相关节点。
			var style = element?.GetFirstChild<P.ShapeStyle>(); // 样式节点。
			var trans = sppr?.GetFirstChild<D.Transform2D>(); // 形变节点。
			var prstGeom = sppr?.GetFirstChild<D.PresetGeometry>(); // 几何图形节点。

			// 设置名称与编号。
			shp.Name = cnvpr.Name;
			shp.Id = (int)cnvpr.Id.Value;

			// 导入锁定相关配置，不同的图形对象其标签不同。
			SetLock(shp, xmlname, obLocks);

			// 判断是否为文本框。
			SetIsTextBox(shp, cnvobpr);

			// 导入形变。
			SetTransform(shp, trans);

			// 设置图形。
			SetPresetGeomtry(shp, prstGeom);
			switch (shp.Type)
			{
				case ShapeType.Common:
					// 设置颜色与线条。
					SetCommonShapeColorAndLine(shp, sldpart, sppr, style, theme, mediaManager);
					break;

				case ShapeType.Group:
					break;
				case ShapeType.Connection:
					break;
				case ShapeType.Picture:
					// 设置颜色与线条。
					SetPictureShapeBlipFillAndLine(shp, sldpart, element, sppr, style, theme, mediaManager);
					break;
				default:
					break;
			}

			return shp;
		}


		public static OpenXmlElement Export(IShape model)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 设置是否为文本框。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="cnvobpr"></param>
		private static void SetIsTextBox(Shape shp, OpenXmlElement cnvobpr)
		{
			var atts = cnvobpr.GetAttributes();
			var rst = (
				from att in atts
				where att.LocalName == "txBox"
				select att).FirstOrDefault();
			if (rst != null && rst.LocalName != null && rst.Value[0] == '1')
				shp.IsTextBox = true;
		}


		/// <summary>
		/// 设置图形。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="presGeom"></param>
		private static void SetPresetGeomtry(Shape shp, D.PresetGeometry presGeom)
		{
			if (presGeom == null) return;
			shp.PresetType = PresetTypeHelper.GetPresetType(presGeom.Preset.Value.ToString());
			if (presGeom.AdjustValueList == null || presGeom.AdjustValueList.ChildElements.Count == 0)
			{
				shp.Adjustments = PresetTypeHelper.GetDefaultAdjustments(shp.PresetType);
			}
			else
			{
				shp.Adjustments = new Adjustments();
				foreach (D.ShapeGuide adj in presGeom.AdjustValueList.ChildElements)
				{
					(shp.Adjustments as List<Emu>).Add(new Emu(adj.Formula.Value.Substring(4)));
				}
			}

		}


		/// <summary>
		/// 设置形变。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="trans"></param>
		private static void SetTransform(Shape shp, D.Transform2D trans)
		{
			if (trans == null) return;
			shp.Transform.RectSize = new RectSize(
				new Emu(trans.Extents.Cx.Value),
				new Emu(trans.Extents.Cy.Value));

			shp.Transform.AnchorTransform = new AnchorTransform(
				0.5, 0.5,
				new Emu(trans.Offset.X.Value),
				new Emu(trans.Offset.Y.Value));

			shp.Transform.Rotation = trans.Rotation?.Value ?? 0;
		}

		/// <summary>
		/// 获取并设置锁定相关的设定。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="xmlname"></param>
		/// <param name="obLocks"></param>
		private static void SetLock(Shape shp, string xmlname, OpenXmlElement obLocks)
		{
			switch (xmlname)
			{
				case "sp":
					shp.Type = ShapeType.Common;
					var spLocks = obLocks as D.ShapeLocks;
					shp.Locks.NoAdjustHandles = spLocks?.NoAdjustHandles ?? false;
					shp.Locks.NoChangeArrowheads = spLocks?.NoChangeArrowheads ?? false;
					shp.Locks.NoChangeAspect = spLocks?.NoChangeAspect ?? false;
					shp.Locks.NoChangeShapeType = spLocks?.NoChangeShapeType ?? false;
					shp.Locks.NoEditPoints = spLocks?.NoEditPoints ?? false;
					shp.Locks.NoGrouping = spLocks?.NoGrouping ?? false;
					shp.Locks.NoMove = spLocks?.NoMove ?? false;
					shp.Locks.NoResize = spLocks?.NoResize ?? false;
					shp.Locks.NoRotation = spLocks?.NoRotation ?? false;
					shp.Locks.NoSelection = spLocks?.NoSelection ?? false;
					shp.Locks.NoTextEdit = spLocks?.NoTextEdit ?? false;

					shp.TextBody = new TextBody();

					break;

				case "cxnSp":
					shp.Type = ShapeType.Connection;
					var cxnSpLocks = obLocks as D.ConnectionShapeLocks;
					shp.Locks.NoAdjustHandles = cxnSpLocks?.NoAdjustHandles ?? false;
					shp.Locks.NoChangeArrowheads = cxnSpLocks?.NoChangeArrowheads ?? false;
					shp.Locks.NoChangeAspect = cxnSpLocks?.NoChangeAspect ?? false;
					shp.Locks.NoChangeShapeType = cxnSpLocks?.NoChangeShapeType ?? false;
					shp.Locks.NoEditPoints = cxnSpLocks?.NoEditPoints ?? false;
					shp.Locks.NoGrouping = cxnSpLocks?.NoGrouping ?? false;
					shp.Locks.NoMove = cxnSpLocks?.NoMove ?? false;
					shp.Locks.NoResize = cxnSpLocks?.NoResize ?? false;
					shp.Locks.NoRotation = cxnSpLocks?.NoRotation ?? false;
					shp.Locks.NoSelection = cxnSpLocks?.NoSelection ?? false;
					break;

				case "grpSp":
					shp.Type = ShapeType.Group;
					var grpSpLocks = obLocks as D.GroupShapeLocks;
					shp.Locks.NoUnGrouping = grpSpLocks?.NoUngrouping ?? false;
					shp.Locks.NoChangeAspect = grpSpLocks?.NoChangeAspect ?? false;
					shp.Locks.NoGrouping = grpSpLocks?.NoGrouping ?? false;
					shp.Locks.NoMove = grpSpLocks?.NoMove ?? false;
					shp.Locks.NoResize = grpSpLocks?.NoResize ?? false;
					shp.Locks.NoRotation = grpSpLocks?.NoRotation ?? false;
					shp.Locks.NoSelection = grpSpLocks?.NoSelection ?? false;
					break;

				case "pic":
					shp.Type = ShapeType.Picture;
					var picLocks = obLocks as D.PictureLocks;
					shp.Locks.NoAdjustHandles = picLocks?.NoAdjustHandles ?? false;
					shp.Locks.NoChangeArrowheads = picLocks?.NoChangeArrowheads ?? false;
					shp.Locks.NoChangeAspect = picLocks?.NoChangeAspect ?? false;
					shp.Locks.NoChangeShapeType = picLocks?.NoChangeShapeType ?? false;
					shp.Locks.NoEditPoints = picLocks?.NoEditPoints ?? false;
					shp.Locks.NoGrouping = picLocks?.NoGrouping ?? false;
					shp.Locks.NoMove = picLocks?.NoMove ?? false;
					shp.Locks.NoResize = picLocks?.NoResize ?? false;
					shp.Locks.NoRotation = picLocks?.NoRotation ?? false;
					shp.Locks.NoSelection = picLocks?.NoSelection ?? false;
					shp.Locks.NoCorp = picLocks?.NoCrop ?? false;
					break;
			}
		}


		/// <summary>
		/// 设置颜色填充与线条。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="sldpart"></param>
		/// <param name="sppr"></param>
		/// <param name="style"></param>
		/// <param name="theme"></param>
		/// <param name="mediaManager"></param>
		private static void SetCommonShapeColorAndLine(
			Shape shp,
			PK.SlidePart sldpart,
			OpenXmlElement sppr,
			OpenXmlElement style,
			Theme theme,
			IMediaManager
			mediaManager)
		{
			shp.Fill.Color = ColorLoader.GetCustomFill(sldpart, theme, sppr, mediaManager);
			shp.Line.Color = ColorLoader.GetCustomLine(sldpart, theme, sppr, mediaManager);
			if (shp.Fill.Color == null)
				shp.Fill.Color = ColorLoader.GetDefaultFill(sldpart, theme, style, mediaManager);
			if (shp.Line.Color == null)
				shp.Line.Color = ColorLoader.GetDefaultLine(sldpart, theme, style, mediaManager);

			shp.Line.Thickness = GetLineThickness(sppr);

		}


		/// <summary>
		/// 设置填充图片与线框颜色。
		/// </summary>
		/// <param name="shp"></param>
		/// <param name="sldpart"></param>
		/// <param name="pic"></param>
		/// <param name="sppr"></param>
		/// <param name="style"></param>
		/// <param name="theme"></param>
		/// <param name="mediaManager"></param>
		private static void SetPictureShapeBlipFillAndLine(
			Shape shp,
			PK.SlidePart sldpart,
			OpenXmlElement pic,
			OpenXmlElement sppr,
			OpenXmlElement style,
			Theme theme,
			IMediaManager
			mediaManager)
		{
			shp.Fill.Color = ColorLoader.GetCustomFill(sldpart, theme, pic, mediaManager);
			shp.Line.Color = ColorLoader.GetCustomLine(sldpart, theme, sppr, mediaManager);

			shp.Line.Thickness = GetLineThickness(sppr);

		}


		private static Emu GetLineThickness(OpenXmlElement sppr)
		{
			var mark = "ln";
			var rst = (from fillnode in sppr
					   where fillnode.LocalName.EndsWith(mark)
					   select fillnode);
			var line = rst.FirstOrDefault();
			if (line == null || (line as D.Outline).Width == null) return Emu.Val1;

			return new Emu((line as D.Outline).Width.Value);
		}





	}
}
